今天就繼續來看const的其他特性。是說本書畢竟是經典老書,有些新語法不在其中,掌握精神之餘應該也要查詢一下是否有現代化手段可以解決。
前面有提到讓整個member function是const,這可以:
而首先有一點值得注意的是,光是function分別有無const屬性,就可以被overload!例如以下:
class TextBlock{
public:
const char& operator[](const std::size_t position) const {return text[position];}
char& operator[](const std::size_t position) const {return text[position];}
}
TextBlock tb("Hello");
std::cout<<tb[0]; // calls non-const operator[]
tb[0] = 'x'; // ok!
void print(const TextBlocl& ctb)
{
std::cpit<<ctb[0]; // calls const operator[]
ctb[0] = 'x'; // error!
}
而我們要定義一個member function是const,其實又可以分成bitwise constness與logical constness兩種。
但這會有一個問題,有些我們認為不是const的function,它卻會通過bitwise的檢查,例如:
class CTextBlock{
public:
char& operator[](std::size_t position) const
{ return pTest[position];} // bitwise conost
private:
char *pTest;
const CTestBlock cctb("hello");
char *pc = &cctb[0];
*pc = 'J'; // cctb become "Jello"!
}
照理說我們宣告const,應該不期待它的值會被改變,結果還是可以改它!只是因為pTest指標沒有改指別處,所以它"物理上"是const。
又或者我們心理上覺得應該是const的,它卻會違反bitwise constness:
class CTextBlock{
public:
std::size_t length() const;
private:
char *pText;
std::size_t textLength;
std::size_t CTestBlock::length() const
{
textLength = std::strlen(pText); // error!
return textLength;
}
QQ只是算了textLength也不行。但除了這個member,我想讓其他member都有const特性不行?
此時,我們就可以用mutable來解決。
class CTextBlock{
public:
std::size_t length() const;
private:
char *pText;
mutable std::size_t textLength;
std::size_t CTestBlock::length() const
{
textLength = std::strlen(pText); // error!
return textLength;
}
std::size_t CTestBlock::length() const
{
textLength = std::strlen(pText); // ok!
return textLength;
}
mutable讓我們可以將特定non-static data member指定為可以修改,但仍然可以讓function pass bitwise constness的檢查,讓我們身心靈達到一致!
而還有另外一個const衍伸的問題,例如說我們前面overload掉一個是const一個不是const的function,但裡面的內容有一堆重複的,那就會讓程式很冗,例如
class TextBlock{
public:
const char& operator[](std::size_t position) const
{
return text[position];
}
char& operator[](std::size_t position)
{
return text[position];
}
private:
std::string text;
此時我們就可以直接call,外加casting來解決,例如
class TextBlock{
public:
const char& operator[](std::size_t position) const
{
return text[position];
}
char& operator[](std::size_t position)
{
return const_cast<char&>(
static_cast<const TestBlock&>(*this)[position] // call const op[]
);
}
private:
std::string text;
雖然用了兩個casting,但他們是安全的,裡面那個casting是要避免無限呼叫到自己,所以要改成const型態,而外面那個則是要再把return的const型態給remove掉。裡面的非const轉const是安全的,所以直接用static_cast
,而要remove掉const型態則只能用const_cast
。這看起來不好看,不過可以避免duplication。
要注意的是應該要像這樣讓 非const去call const的才安全,反過來則不然!因為const就是要確保值不會改變,反過來去call可能就會有不預期的變化了。
貼心重點提醒:
- Declaring something const helps compilers detect usage errors
- Compilers enforce "bitwise" constness, but you should program using "logical" constness
- Use non-const version call the const version to solve code duplication when const and non-const member functions have essentially identical implementations
簡單來說就是 1.能用const盡量用,讓compiler幫你卡關 2.compiler只確保物理上的const,但在寫的時候是會用邏輯上的constness來寫,要注意 3. 要避免duplicate的時候可以讓non-const call const版本
總之,作者誠摯推薦const是個好東西,用了絕不後悔!